package priv.pfz.basedao.mybatis.mapper;

import org.apache.ibatis.annotations.*;
import org.springframework.dao.DuplicateKeyException;
import priv.pfz.basedao.Utils;
import priv.pfz.basedao.mybatis.sqlprovider.*;
import priv.pfz.basedao.sharding.ShardingInfo;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * 极简BaseMapper，继承后直接使用，可满足部分需求（分库分表版）
 * @author yyjxp
 * 2024/1/28 23:09
 */
public interface SimpleShardingBaseMapper<Entity> {
    /**
     * insert一条记录
     * entity中为空的字段不会保存
     * entity中会回写数据库生成的id
     * INSERT INTO table (a,b,c) VALUES (entity.a, entity.b, entity.c)
     */
    @InsertProvider(value = InsertSqlProvider.class, method = "provideShardingSql")
    @Options(useCache = false, useGeneratedKeys = true, keyProperty = "arg0.id", keyColumn = "id")
    int insert(@Nonnull Entity entity, ShardingInfo shardingInfo);

    /**
     * 批量insert多条记录
     * 以第一个entity中非空字段为模板批量保存
     * entity中会回写数据库生成的id
     * INSERT INTO table (a,b,c) VALUES (entity1.a, entity1.b, entity1.c), (entity2.a, entity2.b, entity2.c), ...
     */
    @InsertProvider(value = BatchInsertSqlProvider.class, method = "provideShardingSql")
    @Options(useCache = false, useGeneratedKeys = true, keyProperty = "arg0.id", keyColumn = "id")
    int batchInsert(@Nonnull Iterable<Entity> entities, @Nullable ShardingInfo shardingInfo);

    /**
     * 按id更新一条记录
     * entity中为空的字段不会更新
     * UPDATE table SET a=entity.a, b=entity.b WHERE id=entity.id
     */
    @UpdateProvider(value = UpdateByIdSqlProvider.class, method = "provideShardingSql")
    @Options(useCache = false)
    int updateById(@Nonnull Entity entity, @Nullable ShardingInfo shardingInfo);

    /**
     * 按Entity中标注了@UniqueKey的字段更新一条记录
     * entity中为空的字段不会更新
     * UPDATE table SET a=entity.a, b=entity.b WHERE uk=entity.uk
     */
    @UpdateProvider(value = UpdateByUniqueKeySqlProvider.class, method = "provideShardingSql")
    @Options(useCache = false)
    int updateByUniqueKey(@Nonnull Entity entity, @Nullable ShardingInfo shardingInfo);

    /**
     * 将example匹配的记录更新为entity
     * example中为空的字段不会作为查询条件，entity中为空的字段不会更新
     * UPDATE table SET a=entity.a, b=entity.b WHERE b=example.b AND c=example.c
     */
    @UpdateProvider(value = UpdateByExampleSqlProvider.class, method = "provideShardingSql")
    @Options(useCache = false)
    int updateByExample(@Nonnull Entity entity, @Nonnull Entity example, @Nullable ShardingInfo shardingInfo);

    /**
     * 将examples匹配的记录更新为entity
     * example中为空的字段不会作为查询条件，entity中为空的字段不会更新
     * UPDATE table SET a=entity.a, b=entity.b
     * WHERE (b=example1.b AND c=example1.c) OR (c=example2.c AND d=example2.d) OR ...
     */
    @SelectProvider(value = UpdateByExamplesSqlProvider.class, method = "provideShardingSql")
    @Options(useCache = false)
    int updateByExamples(@Nonnull Entity entity, @Nonnull Iterable<Entity> examples, @Nullable ShardingInfo shardingInfo);

    /**
     * 按id查询一条记录
     * SELECT a,b,c FROM table WHERE id=#{id}
     */
    @SelectProvider(value = SelectByIdSqlProvider.class, method = "provideShardingSql")
    @Options(useCache = false)
    Entity selectById(long id, @Nullable ShardingInfo shardingInfo);

    /**
     * 按ids批量查询多条记录
     * SELECT a,b,c FROM table WHERE id in ids
     */
    @SelectProvider(value = ListByIdsSqlProvider.class, method = "provideShardingSql")
    @Options(useCache = false)
    List<Entity> listByIds(@Nonnull Iterable<Long> ids, @Nullable ShardingInfo shardingInfo);

    /**
     * 按ids批量查询多条记录，返回以id为key的map
     * SELECT a,b,c FROM table WHERE id in ids
     */
    @SelectProvider(value = ListByIdsSqlProvider.class, method = "provideShardingSql")
    @Options(useCache = false)
    @MapKey("id")
    Map<Long, Entity> mapByIds(@Nonnull Iterable<Long> ids, @Nullable ShardingInfo shardingInfo);

    /**
     * 按example中的条件查询第一条记录
     * example中为空的字段不会作为查询条件
     * SELECT a,b,c FROM table WHERE a=example.a AND b=example.b LIMIT 1
     */
    @SelectProvider(value = SelectByExampleSqlProvider.class, method = "provideShardingSql")
    @Options(useCache = false)
    Entity selectByExample(@Nonnull Entity example, @Nullable ShardingInfo shardingInfo);

    /**
     * 按example中的条件查询多条记录
     * example中为空的字段不会作为查询条件
     * SELECT a,b,c FROM table WHERE a=example.a AND b=example.b
     */
    @SelectProvider(value = ListByExampleSqlProvider.class, method = "provideShardingSql")
    @Options(useCache = false)
    List<Entity> listByExample(@Nonnull Entity example, @Nullable ShardingInfo shardingInfo);

    /**
     * 按examples中的条件查询多条记录
     * example中为空的字段不会作为查询条件
     * SELECT a,b,c FROM table WHERE (a=example1.a AND b=example1.b) OR (b=example2.b AND c=example2.c) OR ...
     */
    @SelectProvider(value = ListByExamplesSqlProvider.class, method = "provideShardingSql")
    @Options(useCache = false)
    List<Entity> listByExamples(@Nonnull Iterable<Entity> examples, @Nullable ShardingInfo shardingInfo);

    /**
     * 查询全表数量
     * SELECT count(*) FROM table
     */
    @SelectProvider(value = CountAllSqlProvider.class, method = "provideShardingSql")
    @Options(useCache = false)
    int countAll(@Nullable ShardingInfo shardingInfo);

    /**
     * 查询全表所有记录
     * SELECT a,b,c FROM table
     */
    @SelectProvider(value = ListAllSqlProvider.class, method = "provideShardingSql")
    @Options(useCache = false)
    List<Entity> listAll(@Nullable ShardingInfo shardingInfo);

    /**
     * 按id删除一条记录
     * DELETE FROM table WHERE id=#{id}
     */
    @SelectProvider(value = DeleteByIdSqlProvider.class, method = "provideShardingSql")
    @Options(useCache = false)
    int deleteById(long id, @Nullable ShardingInfo shardingInfo);

    /**
     * 按ids批量删除多条记录
     * DELETE FROM table WHERE id in ids
     */
    @SelectProvider(value = DeleteByIdsSqlProvider.class, method = "provideShardingSql")
    @Options(useCache = false)
    int deleteByIds(@Nonnull Iterable<Long> ids, @Nullable ShardingInfo shardingInfo);

    /**
     * 按example中的条件删除多条记录
     * example中为空的字段不会作为删除条件
     * DELETE FROM table WHERE a=example.a AND b=example.b
     */
    @SelectProvider(value = DeleteByExampleSqlProvider.class, method = "provideShardingSql")
    @Options(useCache = false)
    int deleteByExample(@Nonnull Entity example, @Nullable ShardingInfo shardingInfo);

    /**
     * 按examples中的条件删除多条记录
     * example中为空的字段不会作为删除条件
     * DELETE FROM table WHERE (a=example1.a AND b=example1.b) OR (b=example2.b AND c=example2.c) OR ...
     */
    @SelectProvider(value = DeleteByExamplesSqlProvider.class, method = "provideShardingSql")
    @Options(useCache = false)
    int deleteByExamples(@Nonnull Iterable<Entity> examples, @Nullable ShardingInfo shardingInfo);

    /**
     * 循环插入
     */
    default int loopInsert(@Nonnull Iterable<Entity> entities, @Nullable ShardingInfo shardingInfo) {
        Utils.checkNotNull(entities, "entities不能为null");
        Iterator<Entity> it = entities.iterator();
        int count = 0;
        while (it.hasNext()) {
            ShardingInfo copy = shardingInfo == null ? null : shardingInfo.copy();
            insert(it.next(), copy);
            count++;
        }
        return count;
    }

    /**
     * 尝试插入，唯一键冲突时按id更新
     */
    default int saveById(@Nonnull Entity entity, @Nullable ShardingInfo shardingInfo) {
        try {
            return insert(entity, shardingInfo);
        } catch (DuplicateKeyException e) {
            return updateById(entity, shardingInfo);
        }
    }

    /**
     * 尝试插入，唯一键冲突时按唯一键更新
     */
    default int saveByUniqueKey(@Nonnull Entity entity, @Nullable ShardingInfo shardingInfo) {
        try {
            return insert(entity, shardingInfo);
        } catch (DuplicateKeyException e) {
            return updateByUniqueKey(entity, shardingInfo);
        }
    }

    /**
     * 尝试插入，唯一键冲突时按id更新
     */
    default int batchSaveById(@Nonnull Iterable<Entity> entities, @Nullable ShardingInfo shardingInfo) {
        Utils.checkNotNull(entities, "entities不能为null");
        Iterator<Entity> it = entities.iterator();
        int count = 0;
        while (it.hasNext()) {
            ShardingInfo copy = shardingInfo == null ? null : shardingInfo.copy();
            saveById(it.next(), copy);
            count++;
        }
        return count;
    }

    /**
     * 尝试插入，唯一键冲突时按唯一键更新
     */
    default int batchSaveByUniqueKey(@Nonnull Iterable<Entity> entities, @Nullable ShardingInfo shardingInfo) {
        Utils.checkNotNull(entities, "entities不能为null");
        Iterator<Entity> it = entities.iterator();
        int count = 0;
        while (it.hasNext()) {
            ShardingInfo copy = shardingInfo == null ? null : shardingInfo.copy();
            saveByUniqueKey(it.next(), copy);
            count++;
        }
        return count;
    }
}
